package br.edu.ufcg.dsc.adsd.lotv.core.entites;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import br.edu.ufcg.dsc.adsd.lotv.core.Connectable;
import br.edu.ufcg.dsc.adsd.lotv.core.LANPackage;
import br.edu.ufcg.dsc.adsd.lotv.utils.Constants;
import eduni.simjava.Sim_event;
import eduni.simjava.Sim_system;
import eduni.simjava.distributions.Sim_negexp_obj;

public class Server extends Connectable {

	private List<String> printers;
	private Sim_negexp_obj delayer;
	//Não saquei como pegar algo do simjava para fazer esse sorteio
	private Random selecter;

	public Server(String name, double mean) {
		super(name);
		this.printers = new ArrayList<String>();
		this.delayer = new Sim_negexp_obj(name + Constants.DISTRIBUTION_NAME, mean);
		this.add_generator(this.delayer);
		//Colocando uma semente fixa pra ter insonsistências em cada execução
		//TODO: Trocar esse selecter por uma distribuição do simjava
		this.selecter = new Random(1);
	}
	
	@Override
	public void body() {
		System.out.println("Server ID:" + this.get_id() + " - Ports: " + this.ports);
		findPrinters();
		while(Sim_system.running()) {
			Sim_event e = new Sim_event();
			this.sim_get_next(e);
			LANPackage pkg = LANPackage.extract(e);
			/* POR ALGUMA RAZÃO ESTRANHA CHEGA UNS EVENTOS COM src ID -1 E SEM LANPACKAGE
			 * isso tava quebrando
			 */
			if(e.get_src() >= 0) {
				int type = (Integer)pkg.valueOf(Constants.PACKAGE_TYPE);
				switch (type) {
				case Constants.INSTALL_PRINTER:
					this.installPrinter(e);
					break;
				case Constants.NEW_REQUEST:
					this.requestPrint(e);
					break;
				case Constants.OKAY_RESPONSE:
					this.confirmToClient(e);
					break;
				default:
					break;
				}
			}
			this.sim_completed(e);
		}
	}

	/**
	 * Solicita que impressoras informem que estão funcionando
	 */
	private void findPrinters() {
		LANPackage pkg = new LANPackage(this.get_name(), null);
		pkg.valueOf(Constants.PACKAGE_TYPE, Constants.FIND_PRINTER);
		this.sim_process(this.delayer.sample());
		this.sim_schedule(this.gatewayPort, 0.0, Constants.BROADCAST, pkg);
	};

	/**
	 * Instala uma impressora (registra que ela está ativa)
	 * @param info
	 */
	private void installPrinter(Sim_event info) {
		LANPackage pkg = LANPackage.extract(info);
		this.printers.add(pkg.getSrc());
		this.sim_process(this.delayer.sample());
	}
	
	/**
	 * Processa e devolve para a internet uma resposta que a impressão foi bem
	 * sucedida e está sendo encaminhada para entrega.
	 * @param info
	 */
	private void confirmToClient(Sim_event info) {
		this.sim_process(this.delayer.sample());
	}

	/**
	 * Processa o documento enviado em formato de impressão, escolhe uma
	 * impressora e envia para a LAN almejando tal impressora.
	 * @param info
	 */
	private void requestPrint(Sim_event info) {
		String dst = this.printers.get(this.selectPrinter(info));
		LANPackage outPkg = new LANPackage(this.get_name(), dst);
		outPkg.valueOf(Constants.PACKAGE_TYPE, Constants.PRINT_JOB);
		this.sim_process(this.delayer.sample());
		this.sim_schedule(this.gatewayPort, 0.0, Constants.UNICAST, outPkg);
	}

	/**
	 * Esse método encapsula a disciplina de seleção de impressoras.
	 * @param info
	 * @return Retorna o índice na lista lista de impressoras que deve ser utilizada
	 * para aquele trabalho de impressão.
	 */
	private int selectPrinter(Sim_event info) {
		//TODO Implementar uma disciplina real...
		return this.selecter.nextInt(this.printers.size());
	}

}
